#pragma once

#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include <cerrno>
#include <string>
#include "Protocal.hpp"
#include "ServerCal.hpp"
#include "NoBlock.hpp"
#include "Epoll.hpp"
#include "Socket.hpp"
#include "nocopy.hpp"

uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET); // 读写设置为ET模式

class Connection;
class TcpServer;

using func_t =function< void(std::weak_ptr<Connection>) >;
using except_fun =function<void(std::weak_ptr<Connection>)>;

class Connection
{
public:
    Connection(int sock)
    :sock_(sock)
    {}

    ~Connection()
    {}

    void SetHandle(func_t recv_cb,func_t send_cb,except_fun except_fun)
    {
        recv_cb_=recv_cb;
        send_cb_=send_cb;
        except_cb_=except_fun;
    }

    int Sock()
    {
        return sock_;
    }

    void Appendinbuffer(string str)
    {
        inbuffer+=str;
    }

    void Appendoutbuffer(string info)
    {
        outbuffer+=info;
    }

    string& Inbuffer()
    {
        return inbuffer;
    }

    string& Outbuffer()
    {
        return outbuffer;
    }
    void SetTcp_point(weak_ptr<TcpServer>point)
    {
        tcp_ptr_=point;
    }


private:
    string inbuffer;
    string outbuffer;

public:
    func_t recv_cb_;
    func_t send_cb_;
    except_fun except_cb_;
    uint16_t port_;
    string ip_;
    weak_ptr<TcpServer> tcp_ptr_;
    int sock_;
};

static const int num=64;
class TcpServer:public std::enable_shared_from_this<TcpServer> ,public Nocopy
{
public:
    TcpServer(uint16_t server_port,func_t on_)
    :epoll_ptr(new Epoll())
    ,sock_ptr(new Sock())
    ,quit_(true)
    ,server_port_(server_port)
    ,message_(on_)
    {}

    void Init()
    {
        sock_ptr->Socket();
        sock_ptr->Bind(server_port_);
        SetNoBlock(sock_ptr->Fd());//accpect 没有连接，直接返回
        sock_ptr->Listen();
        
        lg("Info","create listen sock success");
        int fd=sock_ptr->Fd();
        AddConnection(fd,EVENT_IN,bind(&TcpServer::Accpter,this,placeholders::_1),nullptr,nullptr);
    }

    void Loop()
    {
        quit_=false;
        while(!quit_)
        {
            Dispatch();
            Print();
        }

        quit_=true;
    }

    void Dispatch()
    {
        int n=epoll_ptr->Epoll_Wait(revs,64);

        for(int i=0;i<n;i++)
        {
            uint32_t event=revs[i].events;
            int fd=revs[i].data.fd;
            
            if((event&EPOLLIN)&&IsConnection(fd))
            {
                if(cons_[fd]->recv_cb_)
                {
                    cons_[fd]->recv_cb_(cons_[fd]);
                }
            }

            if((event&EPOLLOUT)&&IsConnection(fd))
            {
                if(cons_[fd]->send_cb_)
                {
                    cons_[fd]->send_cb_(cons_[fd]);
                }
            }
        }
    }
    
    void Print()
    {
        cout<<"client sock list:";
        for(auto& iter:cons_)
        {
            cout<<"sock"<<iter.first;
            cout<<" Inbuffer"<<iter.second->Inbuffer()<<endl;
        }
        cout<<endl;
    }

    void  AddConnection(int sock,uint32_t event,func_t recv,func_t send,except_fun except,
    string ip="0.0.0.0",uint16_t port=0)
    {
        shared_ptr<Connection> new_con(new Connection(sock));
        // new_con->recv_cb_=recv;
        // new_con->send_cb_=send;
        // new_con->except_cb_=except;//可以用SetHandler()初始化
        new_con->SetHandle(recv,send,except);
        new_con->SetTcp_point(shared_from_this());//含回一个share_ptr

        new_con->ip_=ip;
        new_con->port_=port;

        cons_.insert(make_pair(sock,new_con));//构建键值，

        epoll_ptr->Epoll_Updata(EPOLL_CTL_ADD,sock,event);//添加事件
    }

    void Accpter(weak_ptr<Connection> con)
    {
        if(con.expired()) return ;//检查是否还能使用
        auto connection=con.lock();//转化为share_ptr使用
        string ip;
        uint16_t port;

        int fd=sock_ptr->Accpect(&ip,&port);//来链接了
        if(fd<0) return ;
        SetNoBlock(fd);

        lg("Info","get a link client info %d %s",port,ip.c_str());

        AddConnection(fd,EVENT_IN,bind(&TcpServer::Recv,this,placeholders::_1)//收到
        ,bind(&TcpServer::Send,this,placeholders::_1)//发送
        ,bind(&TcpServer::Except,this,placeholders::_1)//异常
        ,ip,port);
    }

    void Recv(weak_ptr<Connection> con)
    {
        if(con.expired()) return ;
        auto connection=con.lock();

        char buffer[4028];
        int fd=connection->Sock();

        while(true)
        {
            int n=read(fd,buffer,sizeof(buffer)-1);
            if(n>0)
            {
                buffer[n]=0;
                connection->Appendinbuffer(buffer);
            }
            else if(n==0)
            {
                 lg("Info"," client close... %s,%d error",connection->ip_.c_str(),connection->port_);
                connection->except_cb_(connection);//客户端都不在了，回复不了了，还处理个蛋
                return;
            }
            else
            {
                if(errno==EWOULDBLOCK)//没数据了，稍后尝试
                break;
                else if(errno==EINTR)//因为信号中断,在给一次机会
                continue;
                else
                {
                    lg("Warning"," reve client error %s,%d error",connection->ip_.c_str(),connection->port_);
                    connection->except_cb_(connection);//出错了，不处理了
                    return;
                }
            }
        }

        message_(connection);//处理一下
    }

    void Send(weak_ptr<Connection> con)
    {
        if(con.expired()) return ;
        auto connection=con.lock();
        
        auto&outbuffer=connection->Outbuffer();//方便一会，把发送出去的删掉
        
        while(true)
        {
            int n=write(connection->Sock(),outbuffer.c_str(),outbuffer.size());
            
            if(n>0)
            {
                outbuffer.erase(0,n);
                if(outbuffer.empty()) break;
            }
            else if(n==0) //没数据发送了
            {
                return ;
            }
            else
            {
                if(errno==EWOULDBLOCK) break;//阻塞
                else if(errno==EINTR) continue;//信号中断
                else//异常 
                {
                    lg("Warning"," send client error,%s,%d ",connection->ip_.c_str(),connection->port_);
                    connection->except_cb_(connection);
                    return ;
                }
            }

        }
        if(!outbuffer.empty())
        {
            EnableEvent(connection->Sock(),true,true);//读，写
        }
        {
            EnableEvent(connection->Sock(),true,false);//不能再发了，但是可以关心读
        }
    }

    void Except(weak_ptr<Connection> con)//关心时间，读，写
    {
        if(con.expired()) return ;
        auto connection =con.lock();
        
        epoll_ptr->Epoll_Updata(EPOLL_CTL_DEL,connection->Sock(),0);//执行删除操作，
        close(connection->sock_);//关闭连接

        lg("Debug","close client %d",connection->Sock());
        cons_.erase(connection->Sock());//去掉键值对
    }
    
    void EnableEvent(int sock,bool readable,bool writeable)
    {
        uint32_t event=0;
        event|=(readable?EPOLLIN:0)|(writeable?EPOLLOUT:0);
        epoll_ptr->Epoll_Updata(EPOLL_CTL_MOD,sock,event);
    }

    bool IsConnection(int key)
    {
        auto ret=cons_.find(key);
        if(ret==cons_.end()) return false;
        else return true;
    }

private:
shared_ptr<Epoll> epoll_ptr;
shared_ptr<Sock> sock_ptr;
unordered_map<int,shared_ptr<Connection>> cons_;

bool quit_;
struct epoll_event revs[num];
uint16_t server_port_;
func_t message_;
};